//
//  DoLater v 1.7
//  Russell Lowke, August 4th 2013
//
//  Copyright (c) 2008-2013 Lowke Media
//  see http://www.lowkemedia.com for more information
//  see http://code.google.com/p/lowke/ for code repository
//
//  Permission is hereby granted, free of charge, to any person obtaining a 
//  copy of this software and associated documentation files (the "Software"), 
//  to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//  and/or sell copies of the Software, and to permit persons to whom the 
//  Software is furnished to do so, subject to the following conditions:
// 
//  The above copyright notice and this permission notice shall be included in 
//  all copies or substantial portions of the Software.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER  
//  DEALINGS IN THE SOFTWARE. 
//
//

package com.lowke
{
    import flash.display.Shape;
    import flash.events.Event;
    
    /**
     * DoLater v 1.7
     * Russell Lowke, August 4th 2013
     * 
     * DoLater is used to trigger a callback after the frame has updated.
     * 
     * Unlike traditional programming languages, ActionScript is executed within 
     * the Flash Player. Flash is based on the movie concept of a timeline, 
     * where symbols and code can be placed on various frames and Flash movies 
     * run at specified frame rates. Flash updates the visuals (color, object 
     * placement, size, etc.) at the end of the frame. This gives the 
     * ActionScript in the frame a chance to execute and set up the visual 
     * changes.
     * 
     * The doLater() method, as defined in the Flex mx.core.UIObject, allows you 
     * to queue a function to be called after the frame has been updated. A 
     * function might need to be called on the next update to allow the rest of 
     * the code scheduled for the current update to be executed.
     * 
     * Paradoxically, regular Flash ActionScript 3 has no inbuilt doLater() 
     * function like the Flex UIObject component (Flex mx.core.UIObject).
     * This DoLater class fills that gap.
     * 
     * Usage:
     * <pre>
     *  DoLater.doIt(callBackFunct, ... args);
     * </pre>
     * 
     * Where the function callBackFunct will be called and args passed to it AFTER 
     * the next Event.ENTER_FRAME, once the frame has updated and any actionscript 
     * on the frame has been executed.
     * 
     * 
     * There is also a onEnterFrame() function,
     * <pre>
     *  DoLater.onEnterFrame(callBackFunct, ... args);
     * </pre>
     * 
     * Where the function callBackFunct will be called and args passed to it ON the  
     * next Event.ENTER_FRAME. Sometimes this can be too soon as items on the frame
     * might not have finished updating.
     *
     * @see http://www.lowkemedia.com
     */
    public class DoLater 
    {    
        // a static _ticker is used to listen for ENTER_FRAME events
        private static var _ticker:Shape = new Shape();
        
        //
        // member variables
        private var _callBackFunct:Function;    // function to be called later
        private var _args:Array;                // arguments to be sent to function
        private var _ready:Boolean;             // wait one ENTER_FRAME to ensure the whole frame has loaded
        
        /**
         * The DoLater constructor isn't intended to be called directly,
         * typically you should use the static helper methods DoLater.doIt() 
         * or DoLater.onEnterFrame() when using DoLater.
         *
         * @param callBackFunct Function to be called after the frame has updated.
         * @param args Adittional arguments to be sent to the function callback.
         */
        public function DoLater(callBackFunct:Function,
                                args:Array = null):void 
        {
            _ready = false;
            if (! (callBackFunct is Function))
            {
                throw new Error("DoLater received an invalid callBackFunct.");
            }
            _callBackFunct = callBackFunct;
            _args  = args;
            
            // Note: this listener must be a hard listener and not soft
            //  as it is intended to be the only reference to the DoLater instance
            _ticker.addEventListener(Event.ENTER_FRAME, enterFrameEvent);
        }
        
        private function enterFrameEvent(event:Event):void 
        {    
            if (_ready) 
            {
                // remove the listener
                _ticker.removeEventListener(Event.ENTER_FRAME, enterFrameEvent);
                
                // call the function, passing into it args
                if (_callBackFunct != null) 
                {
                    _callBackFunct.apply(null, _args);
                }
                _callBackFunct = null;
            } 
            else 
            {
                
                // wait one ENTER_FRAME to ensure the frame has loaded
                //  and triggered all ActionScript on the frame.
                _ready = true;
            }
        }
        
        /** Indicates the DoLater is ready to be removed. */
        public function set ready(val:Boolean):void 
        {
            _ready = val;
        }
        
        
        /**
         * Triggers a function and passes to it any arguments after the
         * next frame has updated and all items/code on various MovieClips
         * have been fully executed.
         *
         * @param callBackFunct Function to be called once the next frame has updated.
         * @param args Array of arguments to be sent to the function callback.
         */
        public static function doIt(callBackFunct:Function, ... args):void 
        {
            // DoLater is intended as a throw away reference.
            new DoLater(callBackFunct, args);
        }
        
        
        /**
         * Triggers a function and passes to it any arguments on the
         * next ENTER_FRAME event, this is useful, but its before various 
         * items/code on the next frame have been fully executed.
         *
         * @param callBackFunct Function to be called on the next ENTER_FRAME.
         * @param args Array of arguments to be sent to the function callback.
         */
        public static function onEnterFrame(callBackFunct:Function, ... args):void 
        {
            // DoLater is intended as a throw away reference.
            new DoLater(callBackFunct, args).ready = true;
        }
    }
}